﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.ServiceModel.Syndication;
using System.Xml;
using EPiAbstractions;
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAccess;
using EPiServer.Filters;
using EPiServer.Security;
using PageTypeBuilder;
using StructureMap;

namespace JoelAbrahamsson.FeedImport
{
    [EPiServer.PlugIn.ScheduledPlugIn(DisplayName = "SyndicationFeedImporter")]
    public class SyndicationFeedImporter
    {
        private static IDataFactoryFacade dataFactoryFacade = ObjectFactory.GetInstance<IDataFactoryFacade>();

        public static string Execute()
        {
            try
            {
                IEnumerable<SyndicationFeedRoot> syndicationRoots = GetSyndicationRoots();

                foreach (SyndicationFeedRoot syndicationRoot in syndicationRoots)
                {
                    UpdateFromFeed(syndicationRoot);
                }
            }
            catch (WebException exception)
            {
                throw new Exception(exception.Message + "Stack trace: " + exception.StackTrace + " Response: " + exception.Response.ToString());
            }

            return "The job ran succesfully";
        }

        private static IEnumerable<SyndicationFeedRoot> GetSyndicationRoots()
        {
            PropertyCriteria pageTypeCriteria = new PropertyCriteria();
            pageTypeCriteria.Type = PropertyDataType.PageType;
            pageTypeCriteria.Condition = CompareCondition.Equal;
            pageTypeCriteria.Name = "PageTypeID";
            pageTypeCriteria.IsNull = false;
            pageTypeCriteria.Value = PageTypeResolver.Instance.GetPageTypeID(typeof(SyndicationFeedRoot)).Value.ToString();

            PropertyCriteriaCollection criteria = new PropertyCriteriaCollection();
            criteria.Add(pageTypeCriteria);

            return dataFactoryFacade.FindPagesWithCriteria(PageReference.StartPage, criteria).Cast<SyndicationFeedRoot>();
        }

        private static void UpdateFromFeed(SyndicationFeedRoot syndicationRoot)
        {
            IEnumerable<SyndicationItem> newItems = GetNewItems(syndicationRoot);
            
            SaveNewItems(syndicationRoot, newItems);

            RemoveExcessItems(syndicationRoot);
        }

        private static IEnumerable<SyndicationItem> GetNewItems(SyndicationFeedRoot syndicationRoot)
        {
            IEnumerable<SyndicationFeedItem> existingItems = dataFactoryFacade.GetChildren(syndicationRoot.PageLink).Cast<SyndicationFeedItem>();

            DateTime newest = DateTime.MinValue;
            if(existingItems.Count() > 0)
                newest = existingItems.OrderByDescending(item => item.PublishDate).First().PublishDate;

            SyndicationFeed feed;
            using (XmlReader reader = XmlReader.Create(syndicationRoot.FeedUrl))
            {
                feed = SyndicationFeed.Load(reader);
            }

            return feed.Items.Where(
                item => item.PublishDate.DateTime > newest && existingItems.Count(existingItem => existingItem.Url.Equals(item.Id)) == 0).Take(syndicationRoot.NumberOfItems);
        }

        private static void SaveNewItems(SyndicationFeedRoot syndicationRoot, IEnumerable<SyndicationItem> newItems)
        {
            foreach (SyndicationItem item in newItems)
            {
                int pageTypeID = PageTypeResolver.Instance.GetPageTypeID(typeof(SyndicationFeedItem)).Value;
                SyndicationFeedItem itemPage = (SyndicationFeedItem) dataFactoryFacade.GetDefaultPageData(syndicationRoot.PageLink, pageTypeID);
                
                itemPage.PageName = item.Title.Text;
                itemPage.Title = item.Title.Text;
                if (item.Summary != null)
                    itemPage.Summary = item.Summary.Text;
                itemPage.Url = item.Id;
                itemPage.PublishDate = item.PublishDate.DateTime;

                dataFactoryFacade.Save(itemPage, SaveAction.Publish, AccessLevel.NoAccess);
            }
        }

        private static void RemoveExcessItems(SyndicationFeedRoot syndicationRoot)
        {
            IEnumerable<SyndicationFeedItem> allItems;
            allItems = dataFactoryFacade.GetChildren(syndicationRoot.PageLink).Cast<SyndicationFeedItem>();
            int overFlow = allItems.Count() - syndicationRoot.NumberOfItems;
            if (overFlow < 0)
                overFlow = 0;
            IEnumerable<SyndicationFeedItem> itemsToRemove = allItems.OrderBy(item => item.PublishDate).Take(overFlow);
            foreach (SyndicationFeedItem itemToRemove in itemsToRemove)
            {
                dataFactoryFacade.Delete(itemToRemove.PageLink, true, AccessLevel.NoAccess);
            }
        }
    }
}
